package mdt.program;

import mdt.action.ExitAction;
import org.tigris.gef.base.*;
import org.tigris.gef.event.ModeChangeEvent;
import org.tigris.gef.event.ModeChangeListener;
import org.tigris.gef.graph.presentation.JGraph;
import org.tigris.gef.ui.IStatusBar;
import org.tigris.gef.ui.ToolBar;
import org.tigris.gef.undo.RedoAction;
import org.tigris.gef.undo.UndoAction;
import org.tigris.gef.util.Localizer;
import org.tigris.gef.util.ResourceLoader;
import mdt.action.CopyAction;
import mdt.action.GroupAction;
import mdt.action.*;
import mdt.action.PasteAction;
import mdt.action.SaveAction;
import mdt.action.UngroupAction;
import mdt.figure.FigEditToolbar;
import mdt.model.ModeLabelDragFactory;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Locale;

public class Displayer implements ModeChangeListener {

	JFrame frame;
	ToolBar toolbar;
	JLabel statusBar;
	JTabbedPane jtp;

	private static Displayer instance;
	private FigEditToolbar fToolbar;
	private JLabel warningMsgLabel;

	private JMenuBar menuBar;

	{
		ArrayList<ModeFactory> modeFactories = new ArrayList<ModeFactory>();
		modeFactories.add(new ModeLabelDragFactory());
		modeFactories.add(new ModeSelectFactory());
		modeFactories.add(new ModePopupFactory());
		modeFactories.add(new ModeDragScrollFactory());
		Globals.setDefaultModeFactories(modeFactories);
	}

	private Displayer() {
		frame = new JFrame("Message Design Toolkit");
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.setIconImage(new ImageIcon(FontBoldAction.class.getResource("/resources/app.png")).getImage());
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				(new ExitAction()).actionPerformed(new ActionEvent(frame, ActionEvent.ACTION_PERFORMED, "Exit"));
			}
		});

		Container container = frame.getContentPane();
		container.setLayout(new BorderLayout());

		JPanel mainPanel = new JPanel(new BorderLayout());
		container.add(mainPanel, BorderLayout.CENTER);
		toolbar = new MDTPalette();
		mainPanel.add(toolbar, BorderLayout.NORTH);

		jtp = new JTabbedPane();
		mainPanel.add(jtp, BorderLayout.CENTER);
		jtp.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent e) {
				JTabbedPane tabSource = (JTabbedPane) e.getSource();
				if (tabSource.getSelectedIndex() >= 0) {
					GraphManager.graphSelected((JGraph) tabSource.getComponentAt(tabSource.getSelectedIndex()));
				} else {
					GraphManager.graphSelected(null);
				}
			}
		});

		menuBar = new JMenuBar();
		setUpMenus();
		container.add(menuBar, BorderLayout.NORTH);
		statusBar = new JLabel();
		container.add(statusBar, BorderLayout.SOUTH);

		Globals.setStatusBar(new IStatusBar() {
			@Override
			public void showStatus(String s) {
				statusBar.setText(s);
			}
		});

		warningMsgLabel = new JLabel();
		fToolbar = new FigEditToolbar(0, 0);

		warningMsgLabel = new JLabel();
		warningMsgLabel.setBorder(BorderFactory.createEtchedBorder());
		warningMsgLabel.setOpaque(true);
		warningMsgLabel.setBackground(Color.white);
		warningMsgLabel.setForeground(Color.red);
	}

	protected void setUpMenus() {
		JMenuItem newItem,
				openItem,
				saveItem,
				printItem,
				exitItem;
		JMenuItem deleteItem, copyItem, pasteItem;
		JMenuItem groupItem, ungroupItem;
		JMenuItem toBackItem, backwardItem, toFrontItem, forwardItem;
		JMenuItem undoItem, redoItem;

		JMenu file = new JMenu(Localizer.localize("GefBase", "File"));
		file.setMnemonic('F');
		menuBar.add(file);
		//file.add(new CmdNew());
		newItem = file.add(new NewAction());
		openItem = file.add(new OpenAction());
		saveItem = file.add(new SaveAction());
		file.add(new SaveAsAction());
		PrintAction cmdPrint = new PrintAction();
		printItem = file.add(cmdPrint);
		exitItem = file.add(new ExitAction());

		JMenu edit = new JMenu(Localizer.localize("GefBase", "Edit"));
		edit.setMnemonic('E');
		menuBar.add(edit);

		JMenu select = new JMenu(Localizer.localize("GefBase", "Select"));
		edit.add(select);
		select.add(new CmdSelectNext(false));
		select.add(new CmdSelectNext(true));
		select.add(new CmdSelectInvert());

		edit.addSeparator();

		copyItem = edit.add(new CopyAction());
		copyItem.setMnemonic('C');
		pasteItem = edit.add(new PasteAction());
		pasteItem.setMnemonic('P');

		deleteItem = edit.add(new CmdDeleteFromModel());
		edit.addSeparator();

		undoItem = edit.add(new UndoAction("Undo"));
		undoItem.setMnemonic('U');
		redoItem = edit.add(new RedoAction("Redo"));
		redoItem.setMnemonic('R');
		edit.addSeparator();

		edit.add(new CmdUseReshape());
		edit.add(new CmdUseResize());
		edit.add(new CmdUseRotate());

		JMenu view = new JMenu(Localizer.localize("GefBase", "View"));
		menuBar.add(view);
		view.setMnemonic('V');
		view.add(new CmdSpawn());
		view.add(new CmdShowProperties());
		view.addSeparator();
		view.add(new CmdAdjustGrid());
		view.add(new CmdAdjustGuide());
		view.add(new CmdAdjustPageBreaks());

		JMenu arrange = new JMenu(Localizer.localize("GefBase", "Arrange"));
		menuBar.add(arrange);
		arrange.setMnemonic('A');
		groupItem = arrange.add(new GroupAction());
		groupItem.setMnemonic('G');
		ungroupItem = arrange.add(new UngroupAction());
		ungroupItem.setMnemonic('U');

		JMenu align = new JMenu(Localizer.localize("GefBase", "Align"));
		arrange.add(align);
		align.add(new CmdAlign(CmdAlign.ALIGN_TOPS));
		align.add(new CmdAlign(CmdAlign.ALIGN_BOTTOMS));
		align.add(new CmdAlign(CmdAlign.ALIGN_LEFTS));
		align.add(new CmdAlign(CmdAlign.ALIGN_RIGHTS));
		align.add(new CmdAlign(CmdAlign.ALIGN_H_CENTERS));
		align.add(new CmdAlign(CmdAlign.ALIGN_V_CENTERS));
		align.add(new CmdAlign(CmdAlign.ALIGN_TO_GRID));

		JMenu distribute =
				new JMenu(Localizer.localize("GefBase", "Distribute"));
		arrange.add(distribute);
		distribute.add(new CmdDistribute(CmdDistribute.H_SPACING));
		distribute.add(new CmdDistribute(CmdDistribute.H_CENTERS));
		distribute.add(new CmdDistribute(CmdDistribute.V_SPACING));
		distribute.add(new CmdDistribute(CmdDistribute.V_CENTERS));

		JMenu reorder = new JMenu(Localizer.localize("GefBase", "Reorder"));
		arrange.add(reorder);
		toBackItem = reorder.add(new CmdReorder(CmdReorder.SEND_TO_BACK));
		toFrontItem = reorder.add(new CmdReorder(CmdReorder.BRING_TO_FRONT));
		backwardItem = reorder.add(new CmdReorder(CmdReorder.SEND_BACKWARD));
		forwardItem = reorder.add(new CmdReorder(CmdReorder.BRING_FORWARD));

		JMenu nudge = new JMenu(Localizer.localize("GefBase", "Nudge"));
		arrange.add(nudge);

		KeyStroke ctrlN = KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK);
		KeyStroke ctrlO = KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK);
		KeyStroke ctrlS = KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK);
		KeyStroke ctrlP = KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_MASK);
		KeyStroke altF4 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.ALT_MASK);

		KeyStroke delKey = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
		KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK);
		KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_MASK);
		KeyStroke ctrlG = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_MASK);
		KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_MASK);
		KeyStroke ctrlB = KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_MASK);
		KeyStroke ctrlF = KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_MASK);
		KeyStroke ctrlZ = KeyStroke.getKeyStroke(KeyEvent.VK_Z, KeyEvent.CTRL_MASK);
		KeyStroke ctrlY = KeyStroke.getKeyStroke(KeyEvent.VK_Y, KeyEvent.CTRL_MASK);
		KeyStroke sCtrlB =
				KeyStroke.getKeyStroke(
						KeyEvent.VK_B,
						KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK);
		KeyStroke sCtrlF =
				KeyStroke.getKeyStroke(
						KeyEvent.VK_F,
						KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK);

		newItem.setAccelerator(ctrlN);
		openItem.setAccelerator(ctrlO);
		saveItem.setAccelerator(ctrlS);
		printItem.setAccelerator(ctrlP);
		exitItem.setAccelerator(altF4);

		deleteItem.setAccelerator(delKey);
		//undoItem.setAccelerator(ctrlZ);
		//cutItem.setAccelerator(ctrlX);
		copyItem.setAccelerator(ctrlC);
		pasteItem.setAccelerator(ctrlV);

		undoItem.setAccelerator(ctrlZ);
		redoItem.setAccelerator(ctrlY);

		groupItem.setAccelerator(ctrlG);
		ungroupItem.setAccelerator(ctrlU);

		toBackItem.setAccelerator(sCtrlB);
		toFrontItem.setAccelerator(sCtrlF);
		backwardItem.setAccelerator(ctrlB);
		forwardItem.setAccelerator(ctrlF);
	}

	public JFrame getJFrame() {
		return frame;
	}

	private void setTabComponent(int idx, String title) {
		JGraph graph = (JGraph)jtp.getComponentAt(idx);
		JPanel panel = new JPanel();
		panel.setOpaque(false);
		panel.setBorder(BorderFactory.createEmptyBorder(1,0,1,0));
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));

		panel.add(new JLabel(title));
		panel.add(Box.createHorizontalStrut(5));
		JButton closeButton = new JButton();
		closeButton.setOpaque(false);
		closeButton.setMargin(new Insets(0, 0, 0, 0));
		closeButton.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
		closeButton.setAction(new AbstractAction() {
			{
				putValue(Action.SMALL_ICON, new ImageIcon(Displayer.class.getResource("/resources/close_sign-16.png")));
			}
			@Override
			public void actionPerformed(ActionEvent e) {
				GraphManager.GraphInfo info = GraphManager.getGraphInfo(graph);
				e.setSource(info);
				boolean close = true;
				if (info.isDirty()) {
					int option = JOptionPane.showConfirmDialog(graph,
							"The diagram was changed since last save, save changes?", "Changes", JOptionPane.YES_NO_CANCEL_OPTION);
					if (option == JOptionPane.OK_OPTION) {
						(new SaveAction()).actionPerformed(e);
						if (info.isDirty()) {
							close = false;
						}
					} else if (option == JOptionPane.CANCEL_OPTION) {
						close = false;
					}
				}
				if (close) {
					closeGraphTab(graph);
				}
			}
		});
		panel.add(closeButton);
		jtp.setTabComponentAt(idx, panel);

	}

	public void closeGraphTab(JGraph graph) {
		jtp.remove(graph);
	}

	public void updateTabGraphDirtyFlag(JGraph graph, String title, boolean isDirty) {
		setTabComponent(jtp.indexOfComponent(graph), (isDirty ? "* " : "") + title);
	}

	public void addTab(String title, JGraph graph) {
		jtp.addTab(title, graph);
		setTabComponent(jtp.getTabCount() - 1, title);
	}

	public void selectTab(JGraph graph) {
		if (graph != null) {
			jtp.setSelectedComponent(graph);
		}
	}

	public void show() {
		frame.setPreferredSize(new Dimension(720, 600));
		frame.pack();
		frame.setVisible(true);
		frame.setLocation(500, 420);
	}

	@Override
	public void modeChange(ModeChangeEvent mce) {
		if (!Globals.getSticky() && Globals.mode() instanceof ModeSelect)
			toolbar.unpressAllButtons();
	}

	public static Displayer instance() {
		return instance;
	}

	public void hideEditToolbar() {
		if (fToolbar != null) {
			fToolbar.setTextPane(null);
			fToolbar.setVisible(false);
			fToolbar.setEditor(null);
			//Globals.curEditor().remove(fToolbar);
		}
	}

	public void showEditToolbar(JTextPane pane, int x, int y) {
		fToolbar.setEditor(Globals.curEditor());
		fToolbar.setTextPane(pane);
		fToolbar.setVisible(true);
		fToolbar.setBounds(x, y, 0, 0);
		//Globals.curEditor().add(fToolbar);
	}

	public Dimension getEditToolbarSize() {
		return fToolbar.getSize();
	}

	public void showWarningMessage(Rectangle bounds, String message) {

		message = message.replaceAll("\n", "<br/>&nbsp;&nbsp;");

		int x = bounds.x + 8;
		int y = bounds.y + bounds.height / 2 + 32;

		final Editor ce = Globals.curEditor();
		JComponent drawingPanel = (JPanel) ce.getJComponent();
		Component awtComp = drawingPanel;
		while (!(awtComp instanceof RootPaneContainer) && awtComp != null) {
			awtComp = awtComp.getParent();
		}
		JLayeredPane layeredPane = ((RootPaneContainer) awtComp).getLayeredPane();
		if (warningMsgLabel.getText().length() > 0) {
			message = warningMsgLabel.getText().replace("</html>", "<br/>&ndash;&nbsp;" + message + "</html>");
		} else {
			message = "<html>&ndash;&nbsp;" + message + "</html>";
		}
		warningMsgLabel.setText(message);
		Dimension size = warningMsgLabel.getPreferredSize();
		Rectangle bbox = new Rectangle(x, y, size.width, size.height);
		bbox = SwingUtilities.convertRectangle(drawingPanel, bbox, layeredPane);
		warningMsgLabel.setBounds(bbox);
		layeredPane.add(warningMsgLabel, JLayeredPane.POPUP_LAYER, 0);
	}

	public void dismissWarningMessage() {
		warningMsgLabel.setText("");
		final Editor ce = Globals.curEditor();
		JComponent drawingPanel = (JPanel) ce.getJComponent();
		Component awtComp = drawingPanel;
		while (!(awtComp instanceof RootPaneContainer) && awtComp != null) {
			awtComp = awtComp.getParent();
		}
		JLayeredPane layeredPane = ((RootPaneContainer) awtComp).getLayeredPane();
		layeredPane.remove(warningMsgLabel);
		ce.damageAll();
	}

	public static void main(String[] args) {
		Localizer.addResource(
				"GefBase",
				"org.tigris.gef.base.BaseResourceBundle");
		Localizer.addResource(
				"GefPres",
				"org.tigris.gef.presentation.PresentationResourceBundle");
		Localizer.addLocale(Locale.getDefault());
		Localizer.switchCurrentLocale(Locale.getDefault());
		ResourceLoader.addResourceExtension("gif");
		ResourceLoader.addResourceExtension("png");
		ResourceLoader.addResourceLocation("/org/tigris/gef/Images");
		ResourceLoader.addResourceLocation("/resources");

		instance = new Displayer();
		//instance().postInit();

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				GraphManager.newGraph(GraphManager.loadDemoAtStartup());
			}
		});

		instance.show();
	}
}
